/*
 * Colonia.cpp
 *
 *  Created on: 04/02/2013
 *      Author: vagner
 */

#include "Colonia.h"
#include "RamdomManager.h"
#include <limits>
#include <vector>
#include <sstream>

namespace tp2cn {

bool comparaPorVisitas(const Mediana& m1, const Mediana& m2) {
	return m1.numeroVisitas > m2.numeroVisitas;
}

void ordenaVerticesCandidatos(std::vector<Mediana> &candidatas) {
	std::sort(candidatas.begin(), candidatas.end(), comparaPorVisitas);
}

Colonia::Colonia(int& numMedianas, int numVertices, Grafo* grafo, int& numFormigas,
		float& txInicialFeromonio, float& txEvaporacao) {

	int i;
	this->numMedianas = numMedianas;
	this->numVertices = numVertices;
	this->txEvaporacao = txEvaporacao;
	this->pGrafo = grafo;
	//std::cout<< "tamanho: "<< this->pGrafo->getNumVertices() << std::endl;
	this->melhorSolucao = std::numeric_limits<double_t>::max();
	this->ultimaSolucao = std::numeric_limits<double_t>::max();

	for (i = 0; i < numFormigas; i++) {
		//Criando formigas e colocado-as no vertice-formigueiro
		this->formigueiro.push_back(Formiga(i, (this->numVertices - 1)));
	}

	this->pGrafo->calculaMatrizDistancia();
	this->matrizFeromonio = new MyMatrix(this->numVertices, this->numVertices,
			txInicialFeromonio);
}

void Colonia::definePosicaoInicial(int& MAX_X, int& MAX_Y, int& modoInicial) {
	if (modoInicial == 0) {
		posicao.coorX = RamdomManager::escolheIgualProb(0, MAX_X + 1);
		posicao.coorY = RamdomManager::escolheIgualProb(0, MAX_Y + 1);
		this->posicao.idVertice = (numVertices - 1);
	} else if (modoInicial == 1) {

		this->posicao = this->pGrafo->getPontoMedio();
		this->posicao.idVertice = numVertices - 1;
	}
	this->pGrafo->insereVerticeNaPosicao(this->posicao, (numVertices - 1));
	this->pGrafo->calculaDistanciaVertice(numVertices - 1);
	//this->pGrafo->showMatrizDistancia();

}

Vertice& Colonia::getPosicaoColonia() {
	return this->posicao;
}

void Colonia::selecionaMedianas(void) {

	std::vector<Formiga>::iterator itFormigas;
	int origemFormiga, destinoFormiga;
	std::vector<long double> vetorProbabilidade;

	for (itFormigas = formigueiro.begin(); itFormigas != formigueiro.end();
			itFormigas++) {
		origemFormiga = itFormigas->getPosicaoAtual();
		vetorProbabilidade = calculaVetorProbMedianas(origemFormiga);
		itFormigas->caminha(vetorProbabilidade);
		destinoFormiga = itFormigas->getPosicaoAtual();
		itFormigas->atualizaFeromonio(this->matrizFeromonio, txEvaporacao,
				ultimaSolucao);
		this->pGrafo->defineNovaVisita(destinoFormiga);
	}
	this->Medianas = escolheMedianas(this->pGrafo, this->numMedianas);

}

Colonia::Colonia() {
	// TODO Auto-generated constructor stub

}

Colonia::~Colonia() {
	// TODO Auto-generated destructor stub
}

std::vector<long double> Colonia::calculaVetorProbMedianas(int idVertice) {

	std::vector<long double> vetorProbabilidade;
	int i;
	//int distancia,capacidade;
	float feromonio, distancia, capacidade;
	long double prob; // probabilidadde ainda não normalizada;

	//Maior e menor probabilidade encontrada, utilizado para normalizar
	long double maxProb = std::numeric_limits<double_t>::min();
	long double minProb = std::numeric_limits<double_t>::max();

	for (i = 0; i < (this->numVertices - 1); i++) {

		distancia = this->pGrafo->getDistancia(idVertice, i);
		feromonio = matrizFeromonio->getItem(idVertice, i).valor.realValor;
		capacidade = this->pGrafo->getCapacidade(i);

		prob = (feromonio * capacidade) / distancia;
		std::cout.precision(2);

		if (prob > maxProb) {
			maxProb = prob;
		} else if (prob < minProb) {
			minProb = prob;
		}
		vetorProbabilidade.push_back(prob);
	}

	//Normalizando valores

	for (i = 0; i < (this->numVertices - 1); i++) {
		vetorProbabilidade.at(i) = RamdomManager::normalizaValores(
				vetorProbabilidade.at(i), maxProb, minProb);
	}
	return vetorProbabilidade;
}

std::vector<long double> Colonia::calculaVetorProbAtendimentos(int idVertice, Grafo& grafo) {

	std::vector<long double> vetorProbabilidade;
	int i;
	//int distancia,capacidade;
	long double feromonio, distancia, demanda;
	long double prob; // probabilidadde ainda não normalizada;
	int capacidadeAtual;

	/*
	 * FLAG que recebe o valor 1 caso o vertice não seja atendido ou é atendido pela mesma mediana
	 * do qual a formiga esteja saindo. Caso contrário, ou seja, o vertice é atendido por outra mediana
	 * diferente da origem da formiga, receberá valor zero.
	 */
	int atendimento;

	//Maior e menor probabilidade encontrada, utilizado para normalizar
	double maxProb = std::numeric_limits<double_t>::min();
	double minProb = std::numeric_limits<double_t>::max();

	capacidadeAtual = grafo.getCapacidade(idVertice);

	for (i = 0; i < (this->numVertices - 1); i++) {

		distancia = grafo.getDistancia(idVertice, i);
		feromonio = matrizFeromonio->getItem(idVertice, i).valor.realValor;
		demanda = grafo.getDemanda(i);

		if(i == idVertice){
			atendimento = 0;
		}else if(capacidadeAtual - demanda < 0){
			//Se a demanda do vertice for maior que a capacidade do vertice
			atendimento = 0;
		}else if(!grafo.verticeAtendido(idVertice)){
			atendimento = 5;
		}else if(grafo.getMediana(idVertice) != idVertice){
			atendimento = 1;
		}else{
			atendimento = 0;
		}

		prob = (atendimento * feromonio) / (demanda + distancia) ;
		//std::cout << "Probabilidade calculada: " << prob << std::endl;
		std::cout.precision(2);

		if (prob > maxProb) {
			maxProb = prob;
		} else if (prob < minProb) {
			minProb = prob;
		}
		vetorProbabilidade.push_back(prob);
	}

	//Normalizando valores

	for (i = 0; i < (this->numVertices - 1); i++) {
		vetorProbabilidade.at(i) = RamdomManager::normalizaValores(
				vetorProbabilidade.at(i), maxProb, minProb);
		//std::cout << "Valor normalizado: " << vetorProbabilidade.at(i) << std::endl;
	}
	return vetorProbabilidade;
}

std::vector<Mediana> Colonia::escolheMedianas(Grafo* grafo, int numMedianas) {

	std::vector<Mediana> medianasCandidatas;
	std::vector<Mediana> medianas;
	Vertice verticeCandidato;
	Mediana medianaCandidata;
	int i;

	for (int i = 0; i < this->pGrafo->getNumVertices(); i++) {
		verticeCandidato = this->pGrafo->getVertice(i);
		//Recuperando apenas vertice visitados
		if (verticeCandidato.numeroFormigas > 0) {
			medianaCandidata.idVertice = verticeCandidato.idVertice;
			medianaCandidata.numeroVisitas = verticeCandidato.numeroFormigas;
			medianasCandidatas.push_back(medianaCandidata);
		}
	}

	if ((int) medianasCandidatas.size() == numMedianas) {
		medianas = medianasCandidatas;
	} else {
		tp2cn::ordenaVerticesCandidatos(medianasCandidatas);
		for (i = 0; i < numMedianas; i++) {
			medianas.push_back(medianasCandidatas.at(i));
		}

	}
	return medianas;
}

std::string Colonia::showMedianas(void) {
	std::stringstream strMedianas;
	std::vector<Mediana>::iterator itMediana;

	for (itMediana = this->Medianas.begin(); itMediana != this->Medianas.end();
			itMediana++) {
		strMedianas << (*itMediana).idVertice << " ";
	}
	//strMedianas << std::endl;
	return strMedianas.str();
}

std::string Colonia::showMelhoresMedianas(void) {
	std::stringstream strMedianas;
	std::vector<Mediana>::iterator itMediana;

	for (itMediana = this->melhoresMedianas.begin(); itMediana != this->melhoresMedianas.end();
			itMediana++) {
		strMedianas << (*itMediana).idVertice << " ";
	}
	//strMedianas << std::endl;
	return strMedianas.str();
}

bool Colonia::isMediana(int& posicao) {

	std::vector<Mediana>::iterator itMedianas = this->Medianas.begin();

	while (itMedianas != this->Medianas.end()) {

		if (itMedianas->idVertice == posicao) {
			break;
		} else {
			itMedianas++;
		}
	}

	if (itMedianas != this->Medianas.end()) {
		return true;
	} else {
		return false;
	}

}

void Colonia::reposicionaFormigas() {

	std::vector<Formiga>::iterator itFormigas;
	int posicaoFormiga;
	int posicaoMediana; //Posicao da mediana no vector Medianas
	int novaPosicao;

	for (itFormigas = formigueiro.begin(); itFormigas != formigueiro.end();
			itFormigas++) {

		posicaoFormiga = itFormigas->getPosicaoAtual();
		//Se a posicao da formiga não é mediana
		if (!isMediana(posicaoFormiga)) {

			posicaoMediana = RamdomManager::escolheIgualProb(0,
					numMedianas - 1);
			novaPosicao = this->Medianas.at(posicaoMediana).idVertice;
			itFormigas->caminhaPara(novaPosicao);
		}
	}
}

void Colonia::defineAtendimentos(void) {

	int origemFormiga, destinoFormiga;
	std::vector<Formiga>::iterator itFormigas;
	std::vector<long double> vetorProbabilidade;
	double solucaoParcial = 0.0;
	int novaCapacidade;
	Grafo tempGrafo(*(this->pGrafo));

	for (itFormigas = formigueiro.begin(); itFormigas != formigueiro.end();
			itFormigas++) {
		origemFormiga = itFormigas->getPosicaoAtual();
		vetorProbabilidade = calculaVetorProbAtendimentos(origemFormiga,tempGrafo);
		itFormigas->caminha(vetorProbabilidade);
		destinoFormiga = itFormigas->getPosicaoAtual();
		/*
		 * Se o vértice não é atendido, soma o valor da sua distancia
		 * a solução do problema e define o atendimento.
		 */

		if(!tempGrafo.verticeAtendido(destinoFormiga)){
			solucaoParcial += tempGrafo.getDistancia(origemFormiga,destinoFormiga);
			tempGrafo.setAtendimento(destinoFormiga,origemFormiga);
		}

		novaCapacidade = tempGrafo.getCapacidade(origemFormiga) - tempGrafo.getDemanda(destinoFormiga);
		tempGrafo.setCapacidade(origemFormiga,novaCapacidade);

		itFormigas->atualizaFeromonio(this->matrizFeromonio, txEvaporacao,
				ultimaSolucao);
		tempGrafo.defineNovaVisita(destinoFormiga);
		//tempGrafo.showVertices();
	}
	this->ultimaSolucao = solucaoParcial;
	if(this->ultimaSolucao < this->melhorSolucao){
		this->melhorSolucao = this->ultimaSolucao;
		this->melhoresMedianas = Medianas;
	}
}

double Colonia::getUltimaSolucao(void){
	return this->ultimaSolucao;
}

double Colonia::getMelhorSolucao(void){
	return this->melhorSolucao;
}

void Colonia::redefineGrafo(Grafo& novoGrafo){
	this->pGrafo->showVertices();

	std::cout<< "Novo Grafo"<<std::endl;

	novoGrafo.showVertices();

	*(this->pGrafo) = novoGrafo;
}

void Colonia::reiniciaMatrizFeromonio(float& txInicialFeromonio){
	this->matrizFeromonio->reinicia(txInicialFeromonio);
}

void Colonia::retornaFomigueiro(void){
	std::vector<Formiga>::iterator itFormigas;

	for(itFormigas = this->formigueiro.begin(); itFormigas != this->formigueiro.end(); itFormigas++){
		itFormigas->voltaAoFormigueiro();
	}
}

void Colonia::show(void){
	std::cout << "******************" << std::endl;
	this->pGrafo->showVertices();
	std::cout << "******************" << std::endl;
	this->pGrafo->showMatrizDistancia();
	std::cout << "******************" << std::endl;
	this->matrizFeromonio->show();
	std::cout << "******************" << std::endl;

}

} /* namespace tp2cn */
